package com.frinder.base.akka;

import akka.actor.AbstractActor;
import lombok.Builder;
import lombok.ToString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import scala.Option;
import scala.PartialFunction;
import scala.concurrent.duration.FiniteDuration;
import scala.runtime.BoxedUnit;

import java.util.concurrent.TimeUnit;

import static com.frinder.base.Settings.GET_TRACE_ID_METHOD;
import static com.frinder.base.Settings.TRACE_ID;
import static com.frinder.base.utils.StringUtils.getTraceId;

/**
 * @ClassName BaseActor
 * @Author frinder
 * @Description actor 基类
 * @Date 2019/1/19 15:48
 * @Version 1.0
 */
public abstract class BaseActor extends AbstractActor {

    protected final Logger blog = LoggerFactory.getLogger(getClass());

    @Autowired
    protected Actors actors;

    @ToString(callSuper = true)
    public static class Loop extends AbstractRequest {
        @Builder
        private Loop(String traceId) {
            super(traceId);
        }
    }

    @ToString(callSuper = true)
    public static class Once extends AbstractRequest {
        @Builder
        private Once(String traceId) {
            super(traceId);
        }
    }

    /**
     * 定时任务
     *
     * @param delay
     * @param interval
     */
    protected void schedule(int delay, int interval) {
        this.schedule(Loop.builder().traceId(getTraceId()).build(), delay, interval);
    }

    /**
     * 定时任务
     *
     * @param payload
     * @param delay
     * @param interval
     */
    protected void schedule(Object payload, int delay, int interval) {
        getContext()
                .getSystem()
                .scheduler()
                .schedule(
                        new FiniteDuration(delay, TimeUnit.SECONDS),
                        new FiniteDuration(interval, TimeUnit.SECONDS),
                        getSelf(),
                        payload,
                        getContext().getSystem().dispatcher(),
                        getSelf()
                );
    }

    /**
     * 一次任务
     *
     * @param interval
     */
    protected void scheduleOnce(int interval) {
        this.scheduleOnce(Once.builder().traceId(getTraceId()).build(), interval);
    }

    /**
     * 一次任务
     *
     * @param payload
     * @param interval
     */
    protected void scheduleOnce(Object payload, int interval) {
        getContext()
                .getSystem()
                .scheduler()
                .scheduleOnce(
                        new FiniteDuration(interval, TimeUnit.MILLISECONDS),
                        getSelf(),
                        payload,
                        getContext().getSystem().dispatcher(),
                        getSelf()
                );
    }

    @Override
    public void aroundReceive(PartialFunction<Object, BoxedUnit> receive, Object message) {
        try {
            Object traceId = message.getClass().getMethod(GET_TRACE_ID_METHOD).invoke(message);
            if (null != traceId) {
                MDC.put(TRACE_ID, String.valueOf(traceId));
            }
            blog.debug("*** => AKKA开始处理消息 => 发送者 {} => 接收者 {} 消息 {}", getSender(), getSelf(), message);
        } catch (Exception e) {
        }
        try {
            super.aroundReceive(receive, message);
        } finally {
            try {
                blog.debug("*** <= AKKA处理完成消息 => 发送者 {} => 接收者 {} 消息 {}", getSender(), getSelf(), message);
            } catch (Exception e) {
            }
            MDC.clear();
        }
    }

    @Override
    public void aroundPreStart() {
        blog.debug("aroundReceive : {}, {}", getSender(), getSelf());
        super.aroundPreStart();
    }

    @Override
    public void aroundPostStop() {
        blog.debug("aroundPostStop : {}, {}", getSender(), getSelf());
        super.aroundPostStop();
    }

    @Override
    public void aroundPreRestart(Throwable reason, Option<Object> message) {
        blog.debug("aroundPreRestart : {}, {}, {}", getSender(), getSelf(), message);
        super.aroundPreRestart(reason, message);
    }

    @Override
    public void aroundPostRestart(Throwable reason) {
        blog.error("{}", reason);
        blog.debug("aroundPostRestart : {}, {}", getSender(), getSelf());
        super.aroundPostRestart(reason);
    }

    @Override
    public void preStart() throws Exception {
        blog.debug("preStart : {}, {}", getSender(), getSelf());
        super.preStart();
    }

    @Override
    public void postStop() throws Exception {
        blog.debug("postStop : {}, {}", getSender(), getSelf());
        super.postStop();
    }

    @Override
    public void preRestart(Throwable reason, Option<Object> message) throws Exception {
        blog.error("{}", reason);
        blog.debug("preRestart : {}, {}, {}", getSender(), getSelf(), message);
        super.preRestart(reason, message);
    }

    @Override
    public void postRestart(Throwable reason) throws Exception {
        blog.error("{}", reason);
        blog.debug("postRestart : {}, {}", getSender(), getSelf());
        super.postRestart(reason);
    }

    @Override
    public void unhandled(Object message) {
        blog.error("unhandled : getSender {}, getSelf {}, {}", getSender(), getSelf(), message);
        super.unhandled(message);
    }

    @Override
    public PartialFunction<Object, BoxedUnit> receive() {
        blog.debug("PartialFunction : {}, {}", getSender(), getSelf());
        return super.receive();
    }
}
